home *** CD-ROM | disk | FTP | other *** search
/ HPAVC / HPAVC CD-ROM.iso / HPACK78S.ZIP / mac.c < prev    next >
C/C++ Source or Header  |  1992-12-01  |  27KB  |  917 lines

  1. /****************************************************************************
  2. *                                                                            *
  3. *                            HPACK Multi-System Archiver                        *
  4. *                            ===========================                        *
  5. *                                                                            *
  6. *                            Macintosh-Specific Routines                        *
  7. *                             MAC.C  Updated 10/09/92                        *
  8. *                                                                            *
  9. * This program is protected by copyright and as such any use or copying of    *
  10. *  this code for your own purposes directly or indirectly is highly uncool    *
  11. *                      and if you do so there will be....trubble.                *
  12. *                 And remember: We know where your kids go to school.            *
  13. *                                                                            *
  14. *             Copyright 1992  Peter C. Gutmann.  All rights reserved            *
  15. *                                                                            *
  16. ****************************************************************************/
  17.  
  18. /* There are some special cases to do with handling the Mac filesystem
  19.    interface.  Since all Mac system calls expect Pascal strings, we need to
  20.    convert the strings first.  In addition, although the calls are pretty
  21.    much identical to what the rest of the world uses, the parameter values
  22.    and orders have been swapped around for no logical reason.
  23.  
  24.    All paths are passed around inside HPACK as SLASH-seperated path
  25.    components for simplicity.  For the Mac, SLASH is a special non-character
  26.    code which is used to delimit pathname components.  When an access to a
  27.    directory is necessary, the individual components are extracted and the
  28.    path parsed to return a dirID for the fileName component.  These dirID's
  29.    should not be confused with HPACK dirID's (which work in the same manner
  30.    but have different values)
  31.  
  32.    "You can always pick the DOS person in the Usenet Mac programming
  33.     groups -- they're the ones who want to access the hardware directly".
  34.  
  35.     - Tim Hammett
  36.  
  37.    "You'll have to write the mappings yourself since Apple doesn't want you
  38.     using a portable API ... OursIsBetterFasterSmallerAndImcompatibleAndWell
  39.     LitigateToKeepItIncompatibleSinceWeDontWantYouWritingProgramsForOther
  40.     Platforms, that's Apple".
  41.  
  42.     - Nigel Bree */
  43.  
  44. #include <MacHeaders>
  45. #include <Packages.h>
  46. #include <stdio.h>
  47. #include <stdlib.h>
  48. #include <string.h>
  49. #include "defs.h"
  50. #include "flags.h"
  51. #include "frontend.h"
  52. #include "system.h"
  53. #include "wildcard.h"
  54. #include "hpackio.h"
  55.  
  56. /* The time difference between the Mac and Unix epochs, in seconds */
  57.  
  58. #define MAC_TIME_OFFSET        0x7C245F00L
  59.  
  60. /* The directory ID of the root and current directories */
  61.  
  62. #define MAC_ROOT_DIRID        2
  63. #define MAC_CURRENT_DIRID    0
  64.  
  65. /* Whether we want OS calls to be executed asynchronously or not */
  66.  
  67. #define ASYNC_STATUS        FALSE
  68.  
  69. /* Define to translate from Mac to rest-of-the-world error codes */
  70.  
  71. #define xlatErr(errCode)    ( ( errCode ) == OK ) ? OK : IO_ERROR
  72.  
  73. /* Default vRefNum and dirID */
  74.  
  75. static short defaultVRefArchive, defaultVRefFile;
  76. static long defaultDirID;
  77.  
  78. /* The drive the archive disk is on (for multidisk archives) */
  79.  
  80. static int archiveDrive;
  81.  
  82. /* Pascalise a given filename */
  83.  
  84. static Str255 pFileName;
  85.  
  86. #define pDirName    pFileName            /* Alias for Pascal fileName */
  87. #define pVolName    pFileName            /* Alias for Pascal volume name */
  88.  
  89. static void pascalise( const char *cString )
  90.     {
  91.     strcpy( ( char * ) pFileName, cString );
  92.     CtoPstr( pFileName );
  93.     }
  94.  
  95. /* Determine the default vRefNum and dirID */
  96.  
  97. void initMacInfo( void )
  98.     {
  99. #if 0
  100.     WDPBRec wdInfo;
  101.  
  102.     /* Get vRefNum for current volume */
  103.     if( GetVol( NULL, &defaultVRefArchive ) != OK )
  104.         puts( "initMacInfo: Can't get current vRefNum" );
  105.  
  106.     /* Get dirID for WD on current volume */
  107.     wdInfo.ioCompletion = NULL;    /* No callback routine */
  108.     wdInfo.ioWDVRefNum = defaultVRefArchive;
  109.     wdInfo.ioWDIndex = 0;        /* Use vRefNum for lookup */
  110.     wdInfo.ioWDProcID = 0;        /* Index all WD's */
  111.     if( PBGetWDInfo( &wdInfo, ASYNC_STATUS ) != OK )
  112.         puts( "initMacInfo: Can't get dirID for WD" );
  113.     defaultDirID = wdInfo.ioWDDirID;
  114. #endif /* 0 */
  115.     defaultVRefArchive = 0;
  116.     defaultDirID = 0;
  117.     }
  118.  
  119. /* Determine the vRefNum for a particular path.  This comprises several
  120.    support routines since we have to sort out the volume an archive is on
  121.    right at the start and then strip off the volume name component for
  122.    multi-disk archive handling, since the volume ID will change with each
  123.    new disk.  In addition, findFirst()/Next() bypasses this code and
  124.    defaults to defaultVRefArchive, since by the time it's called the
  125.    volume name component has been stripped */
  126.  
  127. static BOOLEAN isArchiveSearchPath( const char *path )
  128.     {
  129.     int pathLen = strlen( path ), extLen = strlen( HPAK_MATCH_EXT );
  130.  
  131.     /* Check whether this is a path to an archive or a path to a normal file */
  132.     return( ( pathLen > extLen && \
  133.             !strcmp( path + pathLen - extLen, HPAK_MATCH_EXT ) ) ? TRUE : FALSE );
  134.     }
  135.  
  136. static BOOLEAN isArchivePath( const char *path )
  137.     {
  138.     int pathLen = strlen( path ), extLen = strlen( HPAK_EXT );
  139.  
  140.     /* Check whether this is a path to an archive or a path to a normal file */
  141.     return( ( pathLen > extLen && \
  142.             !strcmp( path + pathLen - extLen, HPAK_EXT ) ) ? TRUE : FALSE );
  143.     }
  144.  
  145. int stripVolumeName( char *path )
  146.     {
  147.     int i;
  148.     
  149.     /* Overwrite the volume name at the start of the string if there is one.
  150.        This is necessary since for multidisk archives the volume name may
  151.        change for each disk */
  152.     for( i = 0; path[ i ] && path[ i ] != ':'; i++ );
  153.     if( path[ i ] )
  154.         {
  155.         /* Note the fencepost error in the length arg so we move the '\0' as well */
  156.         memmove( path, path + i + 1, strlen( path + i ) );
  157.         return( i + 1 );
  158.         }
  159.  
  160.     /* No volume name */
  161.     return( 0 );
  162.     }
  163.  
  164. int currentDrive;
  165.  
  166. static short getVRefNum( const char *path )
  167.     {
  168.     char pVolumeName[ MAX_FILENAME ];
  169.     short vRefNum = defaultVRefArchive;
  170.     HVolumeParam volumePB;
  171.     Str255 volName;
  172.     OSErr status;
  173.     long dirID;
  174.     int i;
  175.  
  176.     /* Extract the volume name from the start of the string */
  177.     for( i = 0; path[ i ] && path[ i ] != ':'; i++ );
  178.     if( !path[ i ] )
  179.         {
  180.         /* No volume name, return default vRefNum.  We can have two different
  181.            types of vRefNums, one for the archive path and one for the file
  182.            path (set by the basePath option), so we have to check whether
  183.            we're handling an archive or a normal file */
  184.         if( isArchivePath( path ) || isArchiveSearchPath( path ) )
  185.             return( defaultVRefArchive );
  186.         else
  187.             return( defaultVRefFile );
  188.         }
  189.  
  190.     /* Set up the fields in the parameter block */
  191.     volumePB.ioCompletion = NULL;    /* No callback routine */
  192.     volumePB.ioNamePtr = volName;
  193.     volumePB.ioVolIndex = 0;        /* Index through entries */
  194.  
  195.     /* Search through all mounted volumes looking for a matching volume name */
  196.     do
  197.         {
  198.         volumePB.ioVRefNum = 0;        /* Use ioVolIndex field */
  199.         volumePB.ioVolIndex++;        /* Move to next field */
  200.         status = PBHGetVInfo( &volumePB, ASYNC_STATUS );
  201.         }
  202.     while( ( status != nsvErr ) && ( strnicmp( path, ( char * ) volName + 1, ( int ) *volName ) ) );
  203.     currentDrive = volumePB.ioVDrvInfo;    /* Remember what drive we're on */
  204.  
  205.     return( ( status == OK ) ? volumePB.ioVRefNum : \
  206.             ( isArchivePath( path ) ) ? defaultVRefArchive : defaultVRefFile );
  207.     }
  208.  
  209. void setVolumeRef( char *path, const BOOLEAN isArchivePath )
  210.     {
  211.     short vRefNum = getVRefNum( path );
  212.     int i;
  213.  
  214.     if( isArchivePath )
  215.         {
  216.         /* Set vRefNum from archive path */
  217.         if( ( defaultVRefArchive = vRefNum ) == 0 )
  218.             /* If we're using a default directory, use a default drive as well */
  219.             archiveDrive = 0;
  220.         else
  221.             /* Remember what drive we're on */
  222.             archiveDrive = currentDrive;
  223.         }
  224.     else
  225.         /* Set vRefNum from basePath */
  226.         defaultVRefFile = vRefNum;
  227.     }
  228.  
  229. /* Extract the filename component out of a pathname */
  230.  
  231. static char *getFilenameComponent( const char *pathName )
  232.     {
  233.     char *lastSlashPtr, *pathPtr = ( char * ) pathName;
  234.     int i;
  235.  
  236.     /* Skip the volume name if there is one */
  237.     for( i = 0; pathName[ i ] && pathName[ i ] != ':'; i++ );
  238.     if( pathName[ i ] == ':' )
  239.         pathPtr += i + 1;    /* Point past volume name */
  240.  
  241.     /* Now find the last directory seperator */
  242.     for( lastSlashPtr = pathPtr + strlen( pathPtr ); \
  243.          lastSlashPtr >= pathPtr && *lastSlashPtr != SLASH; \
  244.          lastSlashPtr-- );
  245.     lastSlashPtr++;        /* Fix fencepost error */
  246.  
  247.     return( lastSlashPtr );
  248.     }
  249.  
  250. /* Determine the dirID for a particular path */
  251.  
  252. static long getDirID( short vRefNum, const char *path )
  253.     {
  254.     long dirID = MAC_CURRENT_DIRID;
  255.     int pathLen = strlen( path ), startIndex = 0, endIndex, dirNameLen, i;
  256.     char *pathPtr = ( char * ) path;
  257.     Str255 pDirName;
  258.     CInfoPBRec pb;
  259.  
  260.     /* Skip the volume name if there is one */
  261.     for( i = 0; pathPtr[ i ] && pathPtr[ i ] != ':'; i++ );
  262.     if( pathPtr[ i ] == ':' )
  263.         pathPtr += i + 1;    /* Point past volume name */
  264.     if( *pathPtr == SLASH )
  265.         pathPtr++;            /* Skip slash after volume name */
  266.  
  267.     /* Find the start of the filename component, return if there is no pathname */
  268.     if( !( pathLen = ( getFilenameComponent( pathPtr ) - pathPtr ) ) )
  269.         /* Default to cwd for now */
  270.         return( defaultDirID );
  271.  
  272.     /* Walk down the path getting dirID's for each component as we go */
  273.     endIndex = startIndex;
  274.     pb.dirInfo.ioCompletion = NULL;        /* No callback routine */
  275.     while( endIndex != pathLen )
  276.         {
  277.         /* Extract a directory name out of the pathname */
  278.         while( endIndex < pathLen && pathPtr[ endIndex ] != SLASH )
  279.             endIndex++;
  280.         dirNameLen = endIndex - startIndex;
  281.         strncpy( ( char * ) pDirName + 1, pathPtr + startIndex, dirNameLen );
  282.         pDirName[ 0 ] = dirNameLen;
  283.  
  284.         /* Set up relevant fields in the pb struct */
  285.         pb.dirInfo.ioNamePtr = pDirName;
  286.         pb.dirInfo.ioVRefNum = vRefNum;
  287.         pb.dirInfo.ioFDirIndex = 0;        /* Access directory by name */
  288.         pb.dirInfo.ioDrDirID = dirID;
  289.  
  290.         if( PBGetCatInfo( &pb, ASYNC_STATUS ) != OK )
  291.             printf( "getDirID: Can't stat %s\n", pathPtr + startIndex );
  292.  
  293.         dirID = pb.dirInfo.ioDrDirID;
  294.         startIndex = ++endIndex;    /* Skip SLASH */
  295.         }
  296.  
  297.     /* Default to cwd for now */
  298.     return( dirID );
  299.     }
  300.  
  301. /* Stat a file/directory.  It would be nice to use PBHGetFInfo() for this
  302.    but that only works for files, not directories.  In addition the
  303.    companion PBHSetFInfo() can't set backup times */
  304.  
  305. int stat( const char *fileName, CInfoPBRec *pbPtr )
  306.     {
  307.     short vRefNum = getVRefNum( fileName );
  308.     long dirID = getDirID( vRefNum, fileName );
  309.     OSErr status;
  310.  
  311.     pascalise( getFilenameComponent( fileName ) );
  312.  
  313.     /* Set up file parameter block */
  314.     pbPtr->hFileInfo.ioCompletion = NULL;    /* No callback */
  315.     pbPtr->hFileInfo.ioVRefNum = vRefNum;
  316.     pbPtr->hFileInfo.ioFDirIndex = 0;        /* Get info on given filename */
  317.     pbPtr->hFileInfo.ioFVersNum = 0;        /* Ignore version no */
  318.     pbPtr->hFileInfo.ioDirID = dirID;
  319.     pbPtr->hFileInfo.ioNamePtr = pFileName;
  320.  
  321.     /* Get the file info and reset the dirID field to its proper value */
  322.     status = PBGetCatInfo( pbPtr, ASYNC_STATUS );
  323.     pbPtr->hFileInfo.ioDirID = dirID;
  324.     return( xlatErr( status ) );
  325.     }
  326.  
  327. #ifdef NO_RIFFRAFF
  328.  
  329. /* Keep out all the LC owners ('020 code already keeps the Classics out) */
  330.  
  331. void noRiffRaff( void )
  332.     {
  333.     double x = sin( 0.671279 );
  334.     }
  335. #endif /* NO_RIFFRAFF */
  336.  
  337. /****************************************************************************
  338. *                                                                            *
  339. *                                HPACKIO Functions                            *
  340. *                                                                            *
  341. ****************************************************************************/
  342.  
  343. /* Create a new file.  It would be nice to be able to specify at this point
  344.    what type of file to create, but we may not know until much later on after
  345.    the type has been translated from a foreign OS */
  346.  
  347. FD hcreat( const char *fileName, const int attr )
  348.     {
  349.     short vRefNum = getVRefNum( fileName );
  350.     long dirID = getDirID( vRefNum, fileName );
  351.     FD fRefNum;
  352.     OSErr status;
  353.  
  354.     pascalise( getFilenameComponent( fileName ) );
  355.  
  356.     if( ( status = HCreate( vRefNum, dirID, pFileName, 'TEXT', 'TEXT' ) ) == dupFNErr )
  357.         /* If the file already exists, delete it (yes I know that creat() by
  358.            definition creates a new file, but the Mac version doesn't), then re-
  359.            re-create it */
  360.         if( ( status = HDelete( vRefNum, dirID, pFileName ) ) == OK )
  361.             status = HCreate( vRefNum, dirID, pFileName, 'TEXT', 'TEXT' );
  362.     if( status == OK )
  363.         status = HOpen( vRefNum, dirID, pFileName, ( char ) attr, &fRefNum );
  364.  
  365.     return( ( status == OK ) ? fRefNum : IO_ERROR );
  366.     }
  367.  
  368. /* Open an existing file */
  369.  
  370. FD hopen( const char *fileName, const int mode )
  371.     {
  372.     short vRefNum = getVRefNum( fileName );
  373.     long dirID = getDirID( vRefNum, fileName );
  374.     FD fRefNum;
  375.     OSErr status;
  376.  
  377.     pascalise( getFilenameComponent( fileName ) );
  378.     status = HOpen( vRefNum, dirID, pFileName, ( char ) mode, &fRefNum );
  379.  
  380.     return( ( status == OK ) ? fRefNum : IO_ERROR );
  381.     }
  382.  
  383. /* Close a file */
  384.  
  385. int hclose( const FD theFile )
  386.     {
  387.     return( xlatErr( FSClose( theFile ) ) );
  388.     }
  389.  
  390. /* Read data from a file.  Note that hitting EOF isn't an error since we always
  391.    try and read as much as we can into a buffer */
  392.  
  393. int hread( const FD theFile, void *buffer, const unsigned int count )
  394.     {
  395.     long readCount = ( long ) count;
  396.     OSErr status;
  397.  
  398.     if( ( status = FSRead( theFile, &readCount, buffer ) ) != OK && status != eofErr )
  399.         fileError();
  400.  
  401.     return( ( int ) readCount );
  402.     }
  403.  
  404. /* Write data to a file.  Note that running out of disk space isn't an error
  405.    since we may be doing a multidisk archive */
  406.  
  407. int hwrite( const FD theFile, void *buffer, const unsigned int count )
  408.     {
  409.     long writeCount = ( long ) count;
  410.     Str255 volName;
  411.     int vRefNum;
  412.     long freeBytes;
  413.     OSErr status;
  414.  
  415.     if( ( status = FSWrite( theFile, &writeCount, buffer ) ) != OK && status != dskFulErr )
  416.         fileError();
  417.  
  418.     /* If we've run out of room, find out how much there really is an write that much */
  419.     if( status == dskFulErr && \
  420.         GetVInfo( archiveDrive, &volName, &vRefNum, &freeBytes ) == OK )
  421.         {
  422.         /* Retry the write.  Getting disk full is now an error */
  423.         if( ( status = FSWrite( theFile, &writeCount, buffer ) ) != OK )
  424.             fileError();
  425.         }
  426.  
  427.     return( ( int ) writeCount );
  428.     }
  429.  
  430. /* Seek to a position in a file */
  431.  
  432. long hlseek( const FD theFile, const long offset, const int origin )
  433.     {
  434.     /* The Mac has SEEK_SET = 1, SEEK_CUR = 3, SEEK_END = 2 */
  435.     static short originTbl[] = { 1, 3, 2 };
  436.  
  437.     if( SetFPos( theFile, originTbl[ origin ], offset ) != OK )
  438.         return( -1L );
  439.     else
  440.         return( htell( theFile ) );
  441.     }
  442.  
  443. /* Return the current position in a file */
  444.  
  445. long htell( const FD theFile )
  446.     {
  447.     long filePos;
  448.  
  449.     if( GetFPos( theFile, &filePos ) == OK )
  450.         return( filePos );
  451.     else
  452.         return( -1L );
  453.     }
  454.  
  455. /* Truncate a file at the current position */
  456.  
  457. int htruncate( const FD theFile )
  458.     {
  459.     return( xlatErr( SetEOF( theFile, htell( theFile ) ) ) );
  460.     }
  461.  
  462. /* Remove a file */
  463.  
  464. int hunlink( const char *fileName )
  465.     {
  466.     short vRefNum = getVRefNum( fileName );
  467.     long dirID = getDirID( vRefNum, fileName );
  468.  
  469.     pascalise( getFilenameComponent( fileName ) );
  470.     return( xlatErr( HDelete( vRefNum, dirID, pFileName ) ) );
  471.     }
  472.  
  473. /* Create a directory */
  474.  
  475. int hmkdir( const char *dirName, const int attr )
  476.     {
  477.     short vRefNum = getVRefNum( dirName );
  478.     long dirID = getDirID( vRefNum, dirName );
  479.     long newDirID;
  480.  
  481.     pascalise( getFilenameComponent( dirName ) );
  482.     return( xlatErr( DirCreate( vRefNum, dirID, pDirName, &newDirID ) ) );
  483.     }
  484.  
  485. /* Rename a file */
  486.  
  487. int hrename( const char *oldName, const char *newName )
  488.     {
  489.     short vRefNum = getVRefNum( oldName );
  490.     long dirID = getDirID( vRefNum, oldName );
  491.     char pNewName[ MAX_FILENAME + 1 ];    /* +1 for Pascal string */
  492.  
  493.     pascalise( getFilenameComponent( newName ) );
  494.     memcpy( pNewName, pFileName, MAX_FILENAME );
  495.     pascalise( getFilenameComponent( oldName ) );
  496.     return( xlatErr( HRename( vRefNum, dirID, pFileName, pNewName ) ) );
  497.     }
  498.  
  499. /* Set/change a file/dir's attributes */
  500.  
  501. int hchmod( const char *fileName, const WORD attr )
  502.     {
  503.     CInfoPBRec filePB;
  504.     OSErr status;
  505.  
  506.     /* Get the file info, change the attribute field, then write it back out */
  507.     if( ( status = stat( fileName, &filePB ) ) != ERROR )
  508.         {
  509.         filePB.hFileInfo.ioFlAttrib = attr;
  510.         status = PBSetCatInfo( &filePB, ASYNC_STATUS );
  511.         }
  512.  
  513.     return( xlatErr( status ) );
  514.     }
  515.  
  516. /****************************************************************************
  517. *                                                                            *
  518. *                                HPACKLIB Functions                            *
  519. *                                                                            *
  520. ****************************************************************************/
  521.  
  522. /* Get an input character, no echo */
  523.  
  524. int hgetch( void )
  525.     {
  526.     return( getchar() );        /* Not really "no echo" */
  527.     }
  528.  
  529. /* Allocate/reallocate/free memory (the library malloc() is somewhat flaky) */
  530.  
  531. void *hmalloc( const unsigned long size )
  532.     {
  533.     return( ( void * ) NewPtr( size ) );
  534.     }
  535.  
  536. void hfree( void *buffer )
  537.     {
  538.     DisposPtr( ( Ptr ) buffer );
  539.     }
  540.  
  541. void *hrealloc( void *buffer, const unsigned long size )
  542.     {
  543.     DisposPtr( ( Ptr ) buffer );
  544.     return( ( void * ) NewPtr( size ) );
  545.     }
  546.  
  547. /****************************************************************************
  548. *                                                                            *
  549. *                                SYSTEM Functions                            *
  550. *                                                                            *
  551. ****************************************************************************/
  552.  
  553. /* Set a file's timestamp */
  554.  
  555. void setFileTime( const char *fileName, const LONG fileTime )
  556.     {
  557.     CInfoPBRec filePB;
  558.  
  559.     /* Get the file info, change the time fields, then write it back out */
  560.     if( stat( fileName, &filePB ) != ERROR )
  561.         {
  562.         filePB.hFileInfo.ioFlCrDat = filePB.hFileInfo.ioFlMdDat = \
  563.                                      fileTime + MAC_TIME_OFFSET;
  564.         PBSetCatInfo( &filePB, ASYNC_STATUS );
  565.         }
  566.     }
  567.  
  568. void setCreationTime( const char *fileName, const LONG creationTime )
  569.     {
  570.     CInfoPBRec filePB;
  571.  
  572.     /* Get the file info, change the creation time field, then write it back out */
  573.     if( stat( fileName, &filePB ) != ERROR )
  574.         {
  575.         filePB.hFileInfo.ioFlCrDat = creationTime + MAC_TIME_OFFSET;
  576.         PBHSetFInfo( &filePB, ASYNC_STATUS );
  577.         }
  578.     }
  579.  
  580. void setBackupTime( const char *fileName, const LONG creationTime )
  581.     {
  582.     CInfoPBRec filePB;
  583.  
  584.     /* Get the file info, change the backup time field, then write it back out */
  585.     if( stat( fileName, &filePB ) != ERROR )
  586.         {
  587.         filePB.hFileInfo.ioFlBkDat = creationTime + MAC_TIME_OFFSET;
  588.         PBHSetFInfo( &filePB, ASYNC_STATUS );
  589.         }
  590.     }
  591.  
  592. /* Set a file's version number */
  593.  
  594. void setVersionNumber( const char *fileName, const BYTE versionNumber )
  595.     {
  596.     CInfoPBRec filePB;
  597.  
  598.     /* Get the file info, change the version number field, then write it back out */
  599.     if( stat( fileName, &filePB ) != ERROR )
  600.         {
  601.         filePB.hFileInfo.ioFVersNum = versionNumber;
  602.         PBHSetFInfo( &filePB, ASYNC_STATUS );
  603.         }
  604.     }
  605.  
  606. /* Set a file's type */
  607.  
  608. void setFileType( const char *fileName, const LONG type, const LONG creator )
  609.     {
  610.     CInfoPBRec filePB;
  611.  
  612.     /* Get the file info, change the type/creator fields, then write it back out */
  613.     if( stat( fileName, &filePB ) != ERROR )
  614.         {
  615.         filePB.hFileInfo.ioFlFndrInfo.fdType = type;
  616.         filePB.hFileInfo.ioFlFndrInfo.fdCreator = creator;
  617.         PBHSetFInfo( &filePB, ASYNC_STATUS );
  618.         }
  619.     }
  620.  
  621. /* Set any extra attribute/type/access information needed by an archive */
  622.  
  623. void setExtraInfo( const char *fileName )
  624.     {
  625.     switch( cryptFlags & ( CRYPT_PKE_ALL | CRYPT_CKE_ALL | CRYPT_SIGN_ALL ) )
  626.         {
  627.         case CRYPT_PKE_ALL:
  628.         case CRYPT_CKE_ALL:
  629.             /* Encrypted archive */
  630.             setFileType( fileName, 'HPKC', 'HPAK' );
  631.             break;
  632.  
  633.         case CRYPT_SIGN_ALL:
  634.             /* Secured archive */
  635.             setFileType( fileName, 'HPKS', 'HPAK' );
  636.             break;
  637.  
  638.         case CRYPT_PKE_ALL | CRYPT_SIGN_ALL:
  639.         case CRYPT_CKE_ALL | CRYPT_SIGN_ALL:
  640.             /* Encrypted + secured archive */
  641.             setFileType( fileName, 'HPKB', 'HPAK' );
  642.             break;
  643.  
  644.         default:
  645.             /* Plain archive */
  646.             setFileType( fileName, 'HPAK', 'HPAK' );
  647.         }
  648.     }
  649.  
  650. #ifndef GUI
  651.  
  652. /* The following two functions aren't needed for the GUI version */
  653.  
  654. #define DEFAULT_ROWS        24
  655. #define DEFAULT_COLS        80
  656.  
  657. void getScreenSize( void )
  658.     {
  659.     /* Not really appropriate so just default to 24x80 */
  660.     screenWidth = DEFAULT_COLS;
  661.     screenHeight = DEFAULT_ROWS;
  662.     }
  663.  
  664. int getCountry( void )
  665.     {
  666.     Intl0Hndl panHandle;
  667.     BYTE dateFormat;
  668.  
  669.     /* Extract the date format from the Intl 0 resource info */
  670.     panHandle = ( Intl0Hndl ) IUGetIntl( 0 );
  671.     dateFormat = ( *panHandle )->dateOrder;
  672.  
  673.     return( ( dateFormat < 3 ) ? dateFormat : 0 );
  674.     }
  675. #endif /* GUI */
  676.  
  677. /* Copy extra info (type, icons, etc) from one file to another */
  678.  
  679. void copyExtraInfo( const char *srcFileName, const char *destFileName )
  680.     {
  681.     /* Copy extra info from srcFD to destFD... */
  682.     }
  683.  
  684. /* Find the first/next file in a directory.  Note we have to take special
  685.    care if we're trying to match archives since the volume name component
  686.    has been stripped and we don't want to default to the data file volume */
  687.  
  688. static BOOLEAN getFileInfo( CInfoPBRec *pbPtr, FILEINFO *fileInfo )
  689.     {
  690.     int filenameLength;
  691.     Str255 ioName;
  692.     FInfo *finderInfo = &pbPtr->hFileInfo.ioFlFndrInfo;
  693.     DInfo *dirInfo = &pbPtr->dirInfo.ioDrUsrWds;
  694.     BOOLEAN matched;
  695.     OSErr status;
  696.  
  697.     /* Set up PBRec fields */
  698.     pbPtr->hFileInfo.ioCompletion = NULL;        /* No callback */
  699.     pbPtr->hFileInfo.ioVRefNum = fileInfo->vRefNum;
  700.     if( pbPtr->hFileInfo.ioNamePtr == NULL )
  701.         pbPtr->hFileInfo.ioNamePtr = ioName;    /* Allocate space to store name */
  702.  
  703.     do
  704.         {
  705.         /* Get the information on the directory entry */
  706.         pbPtr->hFileInfo.ioDirID = fileInfo->dirID;    /* Needs to be reset each time */
  707.         matched = FALSE;
  708.         status = IO_ERROR;
  709.         while( status != OK )
  710.             {
  711.             if( ( status = PBGetCatInfo( pbPtr, ASYNC_STATUS ) ) == fnfErr )
  712.                 /* We've run out of files, return */
  713.                 return( FALSE );
  714.  
  715.             /* Move to the next file */
  716.             pbPtr->hFileInfo.ioFDirIndex++;
  717.             }
  718.  
  719.         /* Extract various useful pieces of information */
  720.         fileInfo->isDirectory = ( pbPtr->hFileInfo.ioFlAttrib & 0x10 ) ? TRUE : FALSE;
  721.         filenameLength = pbPtr->hFileInfo.ioNamePtr[ 0 ];
  722.  
  723.         /* Perform an explicit match for the .HPK extension if necessary */
  724.         if( fileInfo->matchArchive )
  725.             {
  726.             /* Look for an extension */
  727.             if( ( filenameLength > strlen( HPAK_EXT ) ) && \
  728.                 ( !stricmp( ( char * ) pbPtr->hFileInfo.ioNamePtr + \
  729.                             filenameLength - strlen( HPAK_EXT ), HPAK_EXT ) ) )
  730.                 matched = !fileInfo->isDirectory;    /* Don't match directories */
  731.                 break;
  732.             }
  733.         else
  734.             /* Sometimes we only want to match files, not directories */
  735.             matched = ( fileInfo->matchAttr == ALLFILES ) ? \
  736.                       !fileInfo->isDirectory : TRUE;
  737.         }
  738.     while( !matched );
  739.  
  740.     /* Copy the info from PBRec into the FILEINFO struct */
  741.     strncpy( fileInfo->fName, ( char * ) pbPtr->hFileInfo.ioNamePtr + 1, filenameLength );
  742.     fileInfo->fName[ filenameLength ] = '\0';
  743.     fileInfo->fTime = pbPtr->hFileInfo.ioFlMdDat - MAC_TIME_OFFSET;
  744.     fileInfo->createTime = pbPtr->hFileInfo.ioFlCrDat - MAC_TIME_OFFSET;
  745.     fileInfo->backupTime = ( pbPtr->hFileInfo.ioFlBkDat ) ? \
  746.                            pbPtr->hFileInfo.ioFlBkDat - MAC_TIME_OFFSET : 0L;
  747.     fileInfo->versionNumber = pbPtr->hFileInfo.ioFVersNum;
  748.     if( fileInfo->isDirectory )
  749.         {
  750.         fileInfo->fAttr = finderInfo->fdFlags;
  751.         fileInfo->type = fileInfo->creator = 0L;
  752.         fileInfo->fSize = fileInfo->resSize = 0L;
  753.         }
  754.     else
  755.         {
  756.         fileInfo->fAttr = finderInfo->fdFlags;
  757.         fileInfo->type = finderInfo->fdType;
  758.         fileInfo->creator = finderInfo->fdCreator;
  759.         fileInfo->resSize = pbPtr->hFileInfo.ioFlRLgLen;
  760.         fileInfo->fSize = pbPtr->hFileInfo.ioFlLgLen;
  761.         }
  762.     fileInfo->entryNo = pbPtr->hFileInfo.ioFDirIndex;
  763.  
  764.     return( TRUE );
  765.     }
  766.  
  767. BOOLEAN findFirst( const char *pathName, const ATTR fileAttr, FILEINFO *fileInfo )
  768.     {
  769.     char *fileName = getFilenameComponent( pathName );
  770.     short vRefNum = ( fileInfo->matchArchive = !strcmp( fileName, MATCH_ARCHIVE ) ) ? \
  771.                     defaultVRefArchive : getVRefNum( pathName );
  772.     long dirID = getDirID( vRefNum, pathName );
  773.     CInfoPBRec pb;
  774.  
  775.     /* Set up vRefNum, dirID, and file match type fields */
  776.     fileInfo->vRefNum = vRefNum;
  777.     fileInfo->dirID = dirID;
  778.     fileInfo->matchAttr = fileAttr;
  779.  
  780.     /* Check if we want to match a particular file or a whole directory */
  781.     if( strcmp( fileName, MATCH_ALL ) && !fileInfo->matchArchive )
  782.         {
  783.         /* Set up PBRec for individual file match */
  784.         pascalise( fileName );
  785.         pb.hFileInfo.ioNamePtr = pFileName;
  786.         pb.hFileInfo.ioFDirIndex = 0;    /* Match individual file */
  787.         }
  788.     else
  789.         {
  790.         /* Set up PBRec for whole directory match */
  791.         pb.hFileInfo.ioNamePtr = NULL;
  792.         pb.hFileInfo.ioFDirIndex = 1;    /* Match first directory entry */
  793.         }
  794.  
  795.     return( getFileInfo( &pb, fileInfo ) );
  796.     }
  797.  
  798. BOOLEAN findNext( FILEINFO *fileInfo )
  799.     {
  800.     CInfoPBRec pb;
  801.  
  802.     /* Set up PBRec fields */
  803.     pb.hFileInfo.ioNamePtr = NULL;
  804.     pb.hFileInfo.ioFDirIndex = fileInfo->entryNo;
  805.  
  806.     return( getFileInfo( &pb, fileInfo ) );
  807.     }
  808.  
  809. /* Functions for opening and closing a file's resource fork */
  810.  
  811. FD openResourceFork( const char *fileName, const int mode )
  812.     {
  813.     short vRefNum = getVRefNum( fileName );
  814.     long dirID = getDirID( vRefNum, fileName );
  815.     FD resourceForkFD;
  816.  
  817.     pascalise( getFilenameComponent( fileName ) );
  818.     return( ( HOpenRF( vRefNum, dirID, pFileName, ( char ) mode, \
  819.                        &resourceForkFD ) == OK ) ? resourceForkFD : IO_ERROR );
  820.     }
  821.  
  822. void closeResourceFork( const FD resourceForkFD )
  823.     {
  824.     FSClose( resourceForkFD );
  825.     }
  826.  
  827. /* Grudgingly yield a smidgeon of CPU time for other processes every now and then */
  828.  
  829. #define FGND_YIELD_TIME    1        /* Yield 1/60 seconds */
  830. #define BGND_YIELD_TIME    5        /* Yield 5/60 seconds */
  831.  
  832. void yieldTimeSlice( void )
  833.     {
  834.     static long yieldTime = 1;
  835.     EventRecord    theEvent;
  836.  
  837.     if( WaitNextEvent( 0xFFFF, &theEvent, yieldTime, NULL ) )
  838.         if( ( theEvent.what == app4Evt ) && ( theEvent.message & 0x01000000 ) )
  839.             yieldTime = ( theEvent.message & 0x00000001 ) ? \
  840.                         FGND_YIELD_TIME : BGND_YIELD_TIME;
  841.     }
  842.  
  843. /* Eject a disk/wait for a new disk to be inserted */
  844.  
  845. void bleuurgghh( void )
  846.     {
  847.     Eject( NULL, defaultVRefArchive );
  848.     }
  849.  
  850. void waitForDisk( void )
  851.     {
  852.     EventRecord    theEvent;
  853.     Point thePoint;
  854.     long freeBytes;
  855.  
  856.     while( TRUE )
  857.         if( WaitNextEvent( 0xFFFF, &theEvent, 0xFFFFFFFFL, NULL ) && ( theEvent.what == diskEvt ) )
  858.             if( HiWord( theEvent.message ) )
  859.                 /* Some sort of error, give the user the option of ejecting
  860.                    or initializing the disk.  If we're running in the BG
  861.                    the FG program will receive the message, otherwise we have
  862.                    to process it */
  863.                 {
  864.                 SetPt( &thePoint, -1, -1 );    /* Centre the dialog on the screen */
  865.                 if( DIBadMount( thePoint, theEvent.message ) == OK )
  866.                     break;
  867.                 }
  868.             else
  869.                 /* Disk was mounted OK, continue */
  870.                 break;
  871.  
  872.     /* We've mounted a new disk, get its volume name and vRefNum */
  873.     GetVInfo( ( int ) LoWord( theEvent.message ), pVolName, \
  874.               &defaultVRefArchive, &freeBytes );
  875.     }
  876.  
  877. /* Lowercase a string */
  878.  
  879. void strlwr( const char *string )
  880.     {
  881.     char *strPtr = ( char * ) string;
  882.     
  883.     while( *strPtr )
  884.         {
  885.         *strPtr = tolower( *strPtr );
  886.         strPtr++;
  887.         }
  888.     }
  889.  
  890. /* Compare string, non-case-sensitive */
  891.  
  892. int strnicmp( const char *src, const char *dest, int length )
  893.     {
  894.     char *srcPtr = ( char * ) src, *destPtr = ( char * ) dest;
  895.     char srcCh, destCh;
  896.  
  897.     while( length-- )
  898.         {
  899.  
  900.         /* Need to be careful with toupper() side-effects */
  901.         srcCh = *srcPtr++;
  902.         srcCh = toupper( srcCh );
  903.         destCh = *destPtr++;
  904.         destCh = toupper( destCh );
  905.  
  906.         if( srcCh != destCh )
  907.             return( srcCh - destCh );
  908.         }
  909.  
  910.     return( 0 );
  911.     }
  912.     
  913. int stricmp( const char *src, const char *dest )
  914.     {
  915.     return( strnicmp( src, dest, strlen( src ) ) );
  916.     }
  917.